/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package listElement;

import td1exo.List;

/**
 *
 * @author Boukry Omar
 */
public class DoubleLinkedList<E> implements List<E> {

    ListElement<E> first;
    ListElement<E> last;

    public DoubleLinkedList() {
        this.first = null;
        this.last = null;
    }

    /*========================================================================*/
    public E getFirst() {
        return first.getElement();
    }

    /*========================================================================*/
    public E getLast() {
        return last.getElement();
    }

    /*========================================================================*/
    public E getNext(Object o) {

        if (first == null || o == null) {
            return null;
        }

        ListElement<E> find = first;
        while (find != null && !find.equals(o)) {
            find = find.getNext();
        }//end while
        return find != null && find.getNext() != null ? find.getNext().getElement() : null;
    }

    /*========================================================================*/
    public E getNext(int index) {

        if (first == null) {
            return null;
        }

        ListElement<E> find = first;
        while (find.getPosition() != index) {
            find = find.getNext();
        }//end while

        return find != null && find.getNext() != null ? find.getNext().getElement() : null;
    }

    /*========================================================================*/
    public E getPrevious(Object o) {
        if (first == null || o == null) {
            return null;
        }

        ListElement<E> find = first;
        while (find != null && !find.equals(o)) {
            find = find.getNext();
        }//end while
        return find != null && find.getNext() != null ? find.getPrevious().getElement() : null;
    }

    /*========================================================================*/
    public E getPrevious(int index) {
        if (first == null) {
            return null;
        }

        ListElement<E> find = first;
        while (find.getPosition() != index) {
            find = find.getNext();
        }//end while

        return find != null && find.getNext() != null ? find.getPrevious().getElement() : null;
    }

    /*========================================================================*/
    public void clear() {
        first = null;
        last = null;
    }

    /*========================================================================*/
    public E get(int index) {

        if (first == null) {
            return null;
        }

        ListElement<E> find = first;
        while (find.getPosition() != index && find != null) {
            find = find.getNext();
        }

        return find.getElement();
    }

    /*========================================================================*/
    public E set(int index, E element) {

        if (first == null) {
            return null;
        }

        boolean modif = false;
        E modified = null;
        ListElement<E> find = first;

        while (modif == false && find != null) {
            if (find.getPosition() == index) {
                modified = find.getElement();
                find.setElement(element);
                modif = true;
            }
            find = find.getNext();
        }

        return modified;
    }

    /*========================================================================*/
    public int size() {
        return last == null ? 0 : last.getPosition() + 1;
    }

    /*========================================================================*/
    public boolean isEmpty() {
        return first == null;
    }

    /*========================================================================*/
    public boolean contains(Object o) {
        boolean b = false;

        if (first == null) {
            return b;
        }

        ListElement<E> find = first;
        while (find != null && b == false) {
            if (find.getElement().equals(o)) {
                b = true;
            } else {
                find = find.getNext();
            }//end else
        }
        return b;
    }

    /*========================================================================*/
    public boolean add(E e) {
        try {
            if (first == null) {
                first = new ListElement(e, null, null, 0);
            } else {
                if (last == null) {
                    first.setNext(last = new ListElement(e, first, null, 1));
                } else {
                    ListElement<E> temp = new ListElement(e, last, null, last.getPosition() + 1);
                    last.setNext(temp);
                    last = temp;
                }//end else
            }// end else

            return true;
        } catch (Exception excp) {
            return false;
        }// end catch
    }//end add(E)

    /*========================================================================*/
    public boolean remove(Object o) {
        boolean removed = false;

        if (o == null || first == null) {
            return false;
        }

        if (first.getElement().equals(o)) {
            first = first.getNext();
            first.setPrevious(null);
            if (first == null) {
                last = null;
            }
            removed = true;
        }


        ListElement<E> find = first;
        while (find != null) {
            if (removed == true) {
                find.setPosition(find.getPosition() - 1);
            }//end if
            else if(removed == false && find.getNext().getElement().equals(o)) {
                if (find.getNext() == last) {
                    find.setNext(null);
                    last = find;
                } else {
                    find.setNext(find.getNext().getNext());
                    find.getNext().setPrevious(find);
                    removed = true;
                }//end else
            }//end if
            find = find.getNext();
        }// end while

        return removed;
    }//end

    /*========================================================================*/
    public void add(int index, E element) {

        if (index >= last.getPosition()) {
            ListElement<E> insert = new ListElement(element, last, null, last.getPosition() + 1);
            last.setNext(insert);
            last = insert;
        } else if (index <= first.getPosition()) {
            ListElement<E> insert = new ListElement(element, null, first, first.getPosition() - 1);
            first.setPrevious(insert);
            first = insert;
        } else {
            ListElement<E> find = first;
            boolean added = false;
            while (find != null) {
                if (added == true) {
                    find.setPosition(find.getPosition() + 1);
                }
                else if(added == false && find.getNext().getPosition() == index) {
                    find.setNext(new ListElement(element, find, find.getNext(), index - 1));
                    added = true;
                }
                find = find.getNext();
            }//end while
        }//end if
    }//end

    /*========================================================================*/
    public E remove(int index) {
        boolean isremoved = false;

        if (first == null) {
            return null;
        }

        E removed = null;
        if (first.getPosition() == index) {
            removed = first.getElement();
            first = first.getNext();
            first.setPrevious(null);
            if (first == null) {
                last = null;
            }
            isremoved = true;
        }

        ListElement<E> find = first;
        while (find != null) {
            if (isremoved == true) {
                find.setPosition(find.getPosition() - 1);
            }
            else if(isremoved == false && find.getNext().getPosition() == index) {
                removed = find.getNext().getElement();
                if (find.getNext().getPosition() == last.getPosition()) {
                    find.setNext(null);
                    last = find;
                } else {
                    find.setNext(find.getNext().getNext());
                    find.getNext().setPrevious(find);
                    isremoved = true;
                }
            }
            find = find.getNext();
        }// end while

        return removed;
    }

    /*========================================================================*/
    public int indexOf(Object o) {
        int index = -1;

        if (first == null) {
            return index;
        }


        ListElement<E> find = first;

        while (find != null || index == -1) {
            if (find.getElement().equals(o)) {
                index = find.getPosition();
            }//end if
            find = find.getNext();
        }// end while

        return index;
    }

    /*========================================================================*/
    public List<E> subList(int fromIndex, int toIndex) {

        List<E> sublist = new DoubleLinkedList();

        if (first == null || last.getPosition() < fromIndex || fromIndex > toIndex) {
            return sublist;
        }

        ListElement<E> find = first;
        while (find != null && find.getPosition() != toIndex) {
            if (find.getPosition() >= fromIndex) {
                sublist.add(find.getElement());
            }
            find = find.getNext();
        }

        return sublist;
    }

    /*========================================================================*/
    @Override
    public String toString() {

        if (first == null) {
            return "Empty DoubleLinkedList !!!";
        }

        StringBuilder stringBuilder = new StringBuilder();
        ListElement<E> find = first;
        stringBuilder.append("DoubleLinked List [\n");
        while (find != null) {
            stringBuilder.append("\t Element ").append(find.getPosition()).append(" : ");
            stringBuilder.append("[ Previous : ").append(find.getPrevious() != null ? find.getPrevious().getElement().toString() : "null").append(" ]");
            stringBuilder.append("<-").append(find.getElement().toString()).append(" ->");
            stringBuilder.append(" [ Next : ").append(find.getNext() != null ? find.getNext().getElement().toString() : "null").append(" ]\n");
            find = find.getNext();
        }
        stringBuilder.append("]");

        return stringBuilder.toString();
    }
}
